using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace HadronData
{
    public class Polygon
        : Geometry
    {
        private Bounds m_bounds;
        private List<Edge> m_listEdges;

        public Polygon()
        {
            m_bounds = new Bounds();
            m_listEdges = new List<Edge>();
        }

        public Polygon(Polygon p_polygon)
        {
            m_listEdges = new List<Edge>();
            foreach (Edge edge in p_polygon.Edges)
                m_listEdges.Add(new Edge(edge));
            m_bounds = p_polygon.Bounds;
        }

        public bool Contains(Vector2 p_vecPoint)
        {
            if (m_listEdges.Count == 0)
                return false;

            Vector2 vecOutside = m_bounds.Min - new Vector2(1.0f);
            Edge edgePoint = new Edge(vecOutside, p_vecPoint);

            bool bIntersects = false;
            foreach (Edge edge in m_listEdges)
                if (edge.Intersects(edgePoint))
                    bIntersects = !bIntersects;

            return bIntersects;
        }

        public bool Intersects(Edge p_edge)
        {
            foreach (Edge edge in m_listEdges)
                if (edge.Intersects(p_edge))
                    return true;
            return false;
        }

        public bool Intersects(Circle p_circle)
        {
            foreach (Edge edge in m_listEdges)
                if (edge.Intersects(p_circle))
                    return true;
            return false;
        }

        public bool Intersects(Polygon p_polygon)
        {
            foreach (Edge edge in p_polygon.Edges)
                if (Intersects(edge))
                    return true;
            return false;
        }

        public void ComputeContactPoints(Edge p_edge, List<ContactPoint> p_listContactPoints)
        {
            foreach (Edge edge in m_listEdges)
                edge.ComputeContactPoints(p_edge, p_listContactPoints);
        }

        public void ComputeContactPoints(Circle p_circle, List<ContactPoint> p_listContactPoints)
        {
            foreach (Edge edge in m_listEdges)
                edge.ComputeContactPoints(p_circle, p_listContactPoints);
        }

        public void ComputeContactPoints(Polygon p_polygon, List<ContactPoint> p_listContactPoints)
        {
            foreach (Edge edge in p_polygon.m_listEdges)
                ComputeContactPoints(edge, p_listContactPoints);
        }

        public void Transform(Transformation p_transformation, Polygon p_polygonSource)
        {
            while (m_listEdges.Count > p_polygonSource.Edges.Count)
                m_listEdges.RemoveAt(m_listEdges.Count - 1);
            while (m_listEdges.Count < p_polygonSource.Edges.Count)
                m_listEdges.Add(new Edge());

            m_bounds.SetEmpty();
            for (int nIndex = 0; nIndex < m_listEdges.Count; nIndex++)
            {
                m_listEdges[nIndex].Transform(p_transformation, p_polygonSource.Edges[nIndex]);
                m_bounds.ExtendTo(m_listEdges[nIndex].Start);
                m_bounds.ExtendTo(m_listEdges[nIndex].End);
            }
        }

        public void UpdateBounds()
        {
            m_bounds.SetEmpty();
            ExtendBounds(ref m_bounds);
        }

        [ContentSerializerIgnore]
        public List<Edge> Edges
        {
            get { return m_listEdges; }
            set { m_listEdges = value; }
        }

        public Vector2[] Vertices
        {
            get
            {
                Vector2[] arrayVertices = new Vector2[m_listEdges.Count];
                for (int nIndex = 0; nIndex < m_listEdges.Count; nIndex++)
                    arrayVertices[nIndex] = m_listEdges[nIndex].Start;
                return arrayVertices;
            }

            set
            {
                if (value.Length < 3)
                    return;

                m_bounds.SetEmpty();
                m_listEdges.Clear();
                Vector2 vecVertex1 = value[value.Length - 1];
                foreach (Vector2 vecVertex2 in value)
                {
                    m_listEdges.Add(new Edge(vecVertex1, vecVertex2));

                    // update bounds
                    m_bounds.ExtendTo(vecVertex2);

                    vecVertex1 = vecVertex2;
                }
            }
        }

        public Color Colour
        {
            set
            {
                foreach (Edge edge in m_listEdges)
                    edge.Colour = value;
            }
        }

        public void Draw(SpriteBatch p_spriteBatch)
        {
            foreach (Edge edge in m_listEdges)
                edge.Draw(p_spriteBatch);
        }

        #region Geometry Interface

        public bool Intersects(Geometry p_geometry)
        {
            if (p_geometry is Edge)
                return Intersects((Edge)p_geometry);
            else if (p_geometry is Circle)
                return Intersects((Circle)p_geometry);
            else if (p_geometry is Polygon)
                return Intersects((Polygon)p_geometry);
            else
                return false;
        }

        public void ComputeContactPoints(Geometry p_geometry,
            List<ContactPoint> p_listContactPoints)
        {
            if (p_geometry is Edge)
                ComputeContactPoints((Edge)p_geometry, p_listContactPoints);
            else if (p_geometry is Circle)
                ComputeContactPoints((Circle)p_geometry, p_listContactPoints);
            else if (p_geometry is Polygon)
                ComputeContactPoints((Polygon)p_geometry, p_listContactPoints);
        }

        public void Transform(Transformation p_transformation)
        {
            m_bounds.SetEmpty();
            foreach (Edge edge in m_listEdges)
            {
                edge.Transform(p_transformation);
                edge.ExtendBounds(ref m_bounds);
            }
        }

        public void Transform(Transformation p_transformation, Geometry p_geometrySource)
        {
            if (!(p_geometrySource is Polygon))
                throw new Exception("Source Geometry must be a Polygon");
            Transform(p_transformation, (Polygon)p_geometrySource);
        }

        public Geometry TransformCopy(Transformation p_transformation)
        {
            Polygon polygon = new Polygon(this);
            polygon.Transform(p_transformation);
            return polygon;
        }

        public void ExtendBounds(ref Bounds p_bounds)
        {
            foreach (Edge edge in m_listEdges)
                edge.ExtendBounds(ref p_bounds);
        }

        public Circle GetBoundingCircle(Vector2 p_vecOrigin)
        {
            Circle circle = new Circle(p_vecOrigin, 0.0f);
            foreach (Edge edge in m_listEdges)
            {
                float fRadius = Math.Max(
                    (edge.Start - p_vecOrigin).Length(),
                    (edge.End - p_vecOrigin).Length());
                circle.Radius = Math.Max(circle.Radius, fRadius);
            }
            return circle;
        }

        public Bounds Bounds
        {
            get { return m_bounds; }
        }

        #endregion
    }
}
